import {
    GET,
    POST,
    PUT,
    DELETE,
    type RequestConfig,
    type OnSuccessType,
    type OnErrorType,
    type ResponseData
} from "@/service/request";

/**
 * 扩展请求配置接口，继承自 RequestConfig
 */
interface ExtendedRequestConfig extends RequestConfig {}

/**
 * useRequest Hook 的配置选项接口
 * @extends Omit<ExtendedRequestConfig, "url" | "method" | "data"> - 继承除 url、method 和 data 外的其他配置项
 */
interface UseRequestOptions extends Omit<ExtendedRequestConfig, "url" | "method" | "data"> {
    // 缓存时间（毫秒）
    cacheTime?: number;
    // 重试次数
    retryCount?: number;
    // 重试延迟（毫秒）
    retryDelay?: number;
    // 成功回调
    onSuccess?: OnSuccessType;
    // 失败回调
    onError?: OnErrorType;
    // 完成回调
    onFinally?: () => void;
    // 是否在页面离开时取消请求
    cancelOnUnmount?: boolean;
}

/**
 * @description 缓存数据存储结构
 * */
const cache = new Map<string, { data: UniNamespace.RequestSuccessCallbackResult; timestamp: number }>();

/**
 * 请求Hook，用于发起网络请求并管理请求状态
 * @param url 请求地址
 * @param method 请求方法，默认为 GET
 * @param options 配置选项，包含缓存、重试、回调等参数
 * @returns 返回包含 run、refresh、cancel 方法和请求状态的对象
 */
export function useRequest(
    url: string,
    method: "GET" | "POST" | "PUT" | "DELETE" = "GET",
    options: UseRequestOptions = {}
) {
    let requestInstance: UniApp.RequestTask | null = null;
    let retryTimer: number | ReturnType<typeof setTimeout> = 0;

    const isLoading = ref(false);
    const isError = ref(false);

    let loadingTimer: number | null = null;

    const {
        cacheTime = 0,
        retryCount = 0,
        retryDelay = 1000,
        onSuccess,
        onError,
        onFinally,
        cancelOnUnmount = true,
        ...restOptions
    } = options;

    let currentRetry = 0;
    let isCanceled = false;

    /**
     * 创建请求标识符，用于缓存键值生成
     * @param params 请求参数
     * @returns 唯一标识符字符串
     */
    const createRequestIdentifier = (params?: string | AnyObject | ArrayBuffer) => {
        return `${url}-${method}-${JSON.stringify(params || {})}`;
    };

    /**
     * 检查是否存在有效缓存数据
     * @param params 请求参数
     * @returns 缓存的数据或 null
     */
    const checkCache = (params?: string | AnyObject | ArrayBuffer) => {
        if (cacheTime <= 0) return null;
        const key = createRequestIdentifier(params);
        const cached = cache.get(key);
        if (cached && Date.now() - cached.timestamp < cacheTime) {
            return cached.data;
        }
        return null;
    };

    /**
     * 设置缓存数据
     * @param data 要缓存的响应数据
     * @param params 请求参数
     */
    const setCache = (data: UniNamespace.RequestSuccessCallbackResult, params?: string | AnyObject | ArrayBuffer) => {
        if (cacheTime <= 0) return;
        const key = createRequestIdentifier(params);
        cache.set(key, {
            data,
            timestamp: Date.now()
        });
    };

    /**
     * 发起请求的核心方法
     * @param params 请求参数
     * @returns Promise<UniNamespace.RequestSuccessCallbackResult>
     */
    const run = async (params?: string | AnyObject | ArrayBuffer) => {
        isCanceled = false;
        isLoading.value = true;
        isError.value = false;

        /**
         * 请求失败处理函数
         * @param error 错误信息
         */
        const onRequestError: OnErrorType = (error) => {
            clearTimeout(retryTimer);
            if (isCanceled) throw new Error("请求被取消...");
            if (currentRetry < retryCount) {
                currentRetry++;
                retryTimer = setTimeout(() => {
                    return run(params);
                }, retryDelay);
            }
            isError.value = true;
            isLoading.value = false;
            onError && onError(error);
            throw error;
        };

        /**
         * @description 检查缓存
         * */
        const cachedData = checkCache(params);
        if (cachedData) {
            clearTimeout(loadingTimer!);
            onSuccess && onSuccess(cachedData.data as ResponseData<string | AnyObject | ArrayBuffer>, cachedData);
            return cachedData;
        }

        let response: UniNamespace.RequestSuccessCallbackResult;

        /**
         * 请求成功处理函数
         * @param data 响应数据
         * @param result 完整响应结果
         */
        const onRequestSuccess: OnSuccessType = (data, result) => {
            response = result;
            if (isCanceled) throw new Error("Request canceled");
            setCache(response, params);

            isLoading.value = false;

            if (data.code === 200) {
                onSuccess && onSuccess(data, response);
            } else {
                onError && onError({ errMsg: "ERROR_CODE: !200" });
            }
        };

        switch (method) {
            case "GET":
                requestInstance = GET(url, params, { ...restOptions }, onRequestSuccess, onRequestError, onFinally);
                break;
            case "POST":
                requestInstance = POST(url, params, { ...restOptions }, onRequestSuccess, onRequestError, onFinally);
                break;
            case "PUT":
                requestInstance = PUT(url, params, { ...restOptions }, onRequestSuccess, onRequestError, onFinally);
                break;
            case "DELETE":
                requestInstance = DELETE(url, params, { ...restOptions }, onRequestSuccess, onRequestError, onFinally);
                break;
            default:
                throw new Error(`Unsupported method: ${method}`);
        }
    };

    /**
     * 刷新请求，重置重试次数后重新执行请求
     * @param params 请求参数
     * @returns Promise<UniNamespace.RequestSuccessCallbackResult>
     */
    const refresh = (params?: string | AnyObject | ArrayBuffer) => {
        currentRetry = 0;
        return run(params);
    };

    /**
     * 取消当前请求
     */
    const cancel = () => {
        clearTimeout(retryTimer);
        isCanceled = true;
        if (loadingTimer) {
            clearTimeout(loadingTimer);
            loadingTimer = null;
        }
        requestInstance && requestInstance.abort();
    };

    if (cancelOnUnmount) {
        onUnmounted(() => {
            cancel();
        });
    }

    return {
        run,
        refresh,
        cancel,
        requestStatus: {
            isLoading,
            isError
        }
    };
}
